iT邦幫忙

2026 iThome 鐵人賽

DAY 20
0

摘要
Day 19 已經把 Quality Test Report、規則覆蓋矩陣與契約版本比較整理成可閱讀證據。Day 20 回頭處理一個更根本的問題:交換契約規則不能看起來像寫死在程式裡的假規則,所以今天把規則啟用與 LOINC / UCUM 允許集合抽成可載入的合作方 contract 檔。

這和「能不能交換」有什麼關係?

FHIR / TW Core validation 可以回答:

這份資料在標準資料結構與 Profile 下是否合法?

但資料交換還會遇到另一層問題:

合作方在這個交換情境下,要求哪些規則一定要檢查?
哪些 LOINC / UCUM 值是這次交換契約允許的?

所以 Day 20 的重點是把「合作方政策」從 rule implementation 裡拆出來。
因此變成:

Java rule classes = 規則引擎實作
contract JSON files = 合作方交換政策
Quality Gate = 用指定 contract 驗證同一份 Bundle 的結果

這樣比較接近真實醫療交換裡的 implementation guide、companion guide、trading partner agreement 或 interface specification 概念。
名稱不一定都叫 contract,但核心都是:標準之外,交換雙方仍會有場域或合作方特定要求。

今天的實作範圍

今天新增或修改的範圍有:

  • src/main/resources/contracts/demo-lab-v1.0.json
  • src/main/resources/contracts/demo-lab-v1.1.json
  • src/main/java/com/twlab/qualitygate/validation/ExchangeContract.java
  • src/main/java/com/twlab/qualitygate/validation/ExchangeContractService.java
  • src/main/java/com/twlab/qualitygate/validation/BundleParseService.java
  • src/main/java/com/twlab/qualitygate/validation/ContractComparisonService.java
  • src/main/java/com/twlab/qualitygate/validation/ContractRule.java
  • src/main/java/com/twlab/qualitygate/validation/LabCode001ObservationLoincRule.java
  • src/main/java/com/twlab/qualitygate/validation/LabUnit002ObservationUcumCodeRule.java
  • src/main/java/com/twlab/qualitygate/web/ParseController.java
  • src/main/resources/templates/index.html
  • src/test/java/com/twlab/qualitygate/validation/*Tests.java
  • src/test/java/com/twlab/qualitygate/web/ParseControllerTests.java
  • README.md

Day 20 做五件事:

新增內建合作方 contract 檔。
讓規則啟用與允許值由 contract 驅動。
讓使用者可上傳合作方 contract JSON 影響本次驗證。
讓升版比較改成需要時才勾選執行,且比較使用者上傳的二至多個 contract 版本。
移除舊的 hardcoded ContractVersion enum。

為什麼不是直接串 Inferno / Touchstone?

Inferno、Touchstone、TestScript 主要用來測 FHIR Server / Client 的互通行為。
它們通常需要 endpoint、API interaction、search、read、authorization 或完整 test kit。

本專案目前的 MVP 輸入是一份貼上或上傳的 lab Bundle
所以 Day 20 不把範圍擴成 server certification。
今天只先把一個概念落地:

交換政策不寫死在程式裡。
同一份 Bundle 可以用不同 contract 驗證。
contract 版本差異可以造成不同 Quality Gate 結果。

這也讓本專案和通用 FHIR validator 有比較明確的分工:
validator.fhir.org / HAPI validator 負責回答「Resource 是否符合 FHIR / Profile」。
Inferno / Touchstone 負責回答「FHIR API / Server 行為是否符合測試情境」。
本專案這一層則聚焦在「資料送出前,是否符合這次合作方交換契約」,並把不符合的地方轉成可修正的 blocking evidence。

Contract 檔長什麼樣?

Day 20 新增兩份內建 contract:

src/main/resources/contracts/demo-lab-v1.0.json
src/main/resources/contracts/demo-lab-v1.1.json

v1.1 的內容重點如下:

{
  "id": "demo-lab-hospital-a",
  "name": "Demo Lab to Hospital A Exchange Contract",
  "version": "1.1",
  "enabledRuleCodes": [
    "LAB-REF-001",
    "LAB-REF-002",
    "LAB-REF-003",
    "LAB-CODE-001",
    "LAB-UNIT-001",
    "LAB-UNIT-002"
  ],
  "allowedLoincCodes": ["2345-7", "718-7"],
  "allowedUcumCodes": ["mg/dL", "mmol/L"]
}

v1.0 和 v1.1 最大差異是:

Contract 啟用規則
demo-lab-hospital-a#1.0 LAB-REF-001LAB-REF-002LAB-REF-003LAB-CODE-001LAB-UNIT-001
demo-lab-hospital-a#1.1 v1.0 全部規則,再加上 LAB-UNIT-002

也就是說,LAB-UNIT-002 不再是 ContractVersion enum 寫死的版本差異。
它現在來自 v1.1 contract file 的 enabledRuleCodes

規則引擎和 contract 的分工

今天把原本混在一起的兩件事拆開。

Java rule class 仍然負責:

  • 到 Bundle 裡找 ObservationDiagnosticReportPatient
  • 判斷 reference 是否能解析。
  • 判斷 Observation.code.coding 是否含有 LOINC。
  • 判斷 valueQuantity.system/code 是否符合 UCUM policy。
  • 產生 RuleResult

Contract file 則負責:

  • 啟用哪些 rule code。
  • 允許哪些 LOINC code。
  • 允許哪些 UCUM code。
  • 代表哪個合作方與版本。

這個分工讓 LAB-CODE-001LAB-UNIT-002 的意義更清楚:

規則 Java 負責 Contract 負責
LAB-CODE-001 檢查 Observation.code.coding 是否有 http://loinc.org 決定允許 2345-7718-7
LAB-UNIT-002 檢查 valueQuantity.system/code 決定允許 mg/dLmmol/L

所以現在不再是:

程式寫死只允許 mg/dL 或 mmol/L。

而是:

目前載入的 demo-lab-hospital-a#1.1 contract 允許 mg/dL 或 mmol/L。

移除 ContractVersion enum

Day 19 的版本比較是靠 ContractVersion enum:

V1_0 -> 啟用五條規則
V1_1 -> 啟用六條規則

這可以跑,但 policy source of truth 在 Java code 裡。
Day 20 移除這個 enum。

現在版本比較改成兩層:

demo-lab-v1.0.json / demo-lab-v1.1.json -> 保留為可重現的 demo contract
使用者上傳二至多個 contract version JSON -> UI 實際執行 comparison

ContractComparisonService 不再問 enum 哪些規則啟用。
它改成接收指定的 contract list,對同一份 Bundle 逐一驗證。
首頁的 Compare contract versions 不再自動拿內建 v1.0 / v1.1 比較;使用者必須上傳二至多個 contract version JSON。

這讓「契約版本比較」更接近真實情境:

同一份資料,在舊合作方契約下能通過。
同一份資料,在新合作方契約下被新增規則阻擋。

首頁顯示載入的 Contract

Day 20 也把 contract metadata 顯示到首頁。
首頁一打開就會看到 Loaded partner contracts,列出目前預設驗證 contract 與 comparison upload 入口:

Default validation: demo-lab-hospital-a#1.1
Current validation: demo-lab-hospital-a#1.1
Comparison upload: Upload two or more contract version JSON files
Contract upload: Optional JSON upload for the current validation only

送出驗證後,Quality Test Report 的 Input summary 也會顯示:

Exchange contract: demo-lab-hospital-a#1.1

Contract version comparison 表格也新增:

Loaded contract

所以使用者不是只看到抽象的 v1.0 / v1.1。
他可以看到實際載入的 contract id / version。
預設 contract 仍然來自 application resources 裡的 bundled contract。
如果使用者上傳合作方 contract JSON,該 contract 只影響本次 Current validation
Contract version comparison 改成需要比較版本時才勾選執行;執行時必須由使用者上傳二至多個 contract version JSON。
第一個上傳的 contract 會作為 baseline,後續版本會和 baseline 比出新增的 failed rules。

https://ithelp.ithome.com.tw/upload/images/20260821/20177913dBP35x0Etd.png

上傳合作方 Contract

Day 20 也把「載入合作方要求」做成可操作的 upload workflow。
Input Bundle 表單現在除了 Upload Bundle JSON,也多了:

Optional partner contract JSON

上傳的 contract JSON 會經過最小檢查:

  • 必須能 parse 成 ExchangeContract
  • 必須有 idnameversion
  • enabledRuleCodes 不可為空。
  • enabledRuleCodes 只能引用目前系統真的有實作的 rule code。

目前這個 upload 和 comparison 是刻意限制範圍的 MVP:

uploaded contract -> 只影響本次 Current validation
Compare contract versions -> 勾選後才讀取使用者上傳的二至多個 contract version JSON

這樣可以同時做到兩件事:
第一,合作方可提供自己的允許 LOINC / UCUM 集合來驗證本次 Bundle。
第二,升版比較只在使用者真的提供版本檔時執行,不會拿內建版本假設合作方契約。

https://ithelp.ithome.com.tw/upload/images/20260821/20177913sN9HfRTpnW.png

Contract version comparison:需要時才讀使用者上傳的多版 contract

observation-quantity-wrong-ucum-system.json 驗證、勾選 Compare contract versions,並上傳 demo-lab-v1.0.json / demo-lab-v1.1.json 兩份 contract 時,結果仍然維持 Day 19 的示範效果:

v1.0 -> 沒有啟用 LAB-UNIT-002
v1.1 -> 啟用 LAB-UNIT-002 且失敗

但 Day 20 後,這個差異來源變成使用者上傳的 contract file:

Contract Gate Failed rule
demo-lab-hospital-a#1.0 PASSED None
demo-lab-hospital-a#1.1 BLOCKED LAB-UNIT-002

Upgrade blocker evidence 仍然列出可修正證據:

Path: Observation/obs-wrong-ucum-system.valueQuantity.system/code
Actual: http://example.org/local-units|mg/dL
Expected: Observation.valueQuantity.system must be http://unitsofmeasure.org and code must be allowed by the exchange contract.

這次要注意一個細節:
Expected 裡的 allowed value 不再是 rule class 的固定常數。
suggestion 會依 contract 的 allowedUcumCodes 組出目前允許值。

https://ithelp.ithome.com.tw/upload/images/20260821/20177913isTUfROWPE.png

自動化驗證

本機 Maven 測試:

./mvnw test

結果:

Tests run: 65, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS

https://ithelp.ithome.com.tw/upload/images/20260821/20177913BusPCGuqYv.png

Day 20 新增或調整的測試確認:

  • ExchangeContractServiceTests 可以載入 v1.0 / v1.1 contract。
  • v1.0 不啟用 LAB-UNIT-002
  • v1.1 啟用 LAB-UNIT-002
  • LOINC / UCUM 允許值從 contract 讀取。
  • ContractComparisonServiceTests 保持同一份 UCUM 錯誤資料在 v1.0 / v1.1 下結果不同。
  • ContractScenarioCaseTests 的四組代表情境維持通過。
  • Controller 測試確認首頁會顯示 active contract 與 comparison loaded contract。
  • Controller 測試確認上傳的 custom contract 會影響本次驗證。
  • Controller 測試確認上傳 contract 引用未知 rule code 時會顯示錯誤。
  • Controller 測試確認一般驗證不會顯示 Contract version comparison
  • Controller 測試確認勾選 Compare contract versions 且上傳兩份 contract version JSON 才會顯示升版比較。
  • Controller 測試確認勾選 Compare contract versions 但未上傳至少兩份 contract 時會顯示錯誤。

今天完成了什麼

  • 新增 ExchangeContract model。
  • 新增 ExchangeContractService 從 classpath 載入內建 contract。
  • 新增 demo-lab-v1.0.json
  • 新增 demo-lab-v1.1.json
  • 移除 ContractVersion enum。
  • BundleParseService 改用 ExchangeContract 執行驗證。
  • ContractComparisonService 改成可接收指定 contract list 做多版本比較。
  • ContractRule 介面改成接收 ExchangeContract
  • LAB-CODE-001 改讀 allowedLoincCodes
  • LAB-UNIT-002 改讀 allowedUcumCodes
  • 首頁顯示 active exchange contract。
  • 首頁提供 optional partner contract JSON upload。
  • 首頁提供 contract versions multi-file upload。
  • 首頁提供 Compare contract versions checkbox。
  • Contract comparison 改成勾選後才執行,且比較使用者上傳的二至多個 contract versions。
  • README 補上 Partner Exchange Contracts 說明。
  • ./mvnw test 通過,測試數 65。

Day 20 尚未處理:

  • Contract JSON Schema validation。
  • 更完整的 contract 錯誤導引 UI。
  • 多合作方選單。
  • 完整 terminology server validation。
  • 完整 Change Manifest。
  • COMPATIBLE / EXPECTED_BREAKING_CHANGE / UNEXPECTED_REGRESSION 三分類。
  • History 或治理平台。

目前的 MVP 進度:

validation-flow
├─ JSON parse                                  完成
├─ FHIR R4 parse                               完成
├─ FHIR R4 validation                          完成
├─ TW Core validation / safe NOT_EVALUATED      完成
├─ Partner exchange contract loading
│  ├─ demo-lab-v1.0.json                       完成
│  ├─ demo-lab-v1.1.json                       完成
│  ├─ enabledRuleCodes                         完成
│  ├─ allowedLoincCodes                        完成
│  ├─ allowedUcumCodes                         完成
│  └─ optional uploaded contract                完成最小版
├─ Exchange contract rules
│  ├─ LAB-REF-001                              完成並由 contract 啟用
│  ├─ LAB-REF-002                              完成並由 contract 啟用
│  ├─ LAB-REF-003                              完成並由 contract 啟用
│  ├─ LAB-CODE-001                             完成,允許值由 contract 提供
│  ├─ LAB-UNIT-001                             完成並由 contract 啟用
│  └─ LAB-UNIT-002                             完成,允許值由 contract 提供
├─ Quality Gate                                完成最小版
├─ Contract comparison
│  ├─ v1.0 / v1.1 contract file loading         完成
│  ├─ comparison service test                   完成
│  ├─ homepage comparison display               完成
│  ├─ compare checkbox                          完成
│  ├─ uploaded 2+ version comparison             完成
│  └─ upgrade blocker evidence display          完成
├─ Scenario test pack
│  ├─ v1.0 / v1.1 representative cases          完成 4 例
│  └─ NOT_APPLICABLE scenario fixture           完成 1 例
├─ Homepage Quality Test Report                 完成最小版
└─ Reproducible delivery
   ├─ Dockerfile                                完成最小版
   ├─ Docker Compose                            完成最小版並驗證啟動
   └─ GitHub Actions CI                         完成最小版

下一步預計處理:

Contract JSON Schema + contract diff report / export

Repository:twcore-data-quality-gate


上一篇
Day19 - 驗證覆蓋證據與整理 Quality Test Report
下一篇
Day21 - 把 Demo Contract 對齊真實 FHIR 交換契約語境
系列文
醫療資料通過標準驗證,就真的能交換嗎?——30 天打造 TW Core 資料品質閘門21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言